package com.ourway.service.impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.liuweiheng.base.constant.redis.GlobalStrRedisConstant;
import com.liuweiheng.base.utils.BeanCopyUtil;
import com.liuweiheng.base.utils.GlobalStrRedisUtil;
import com.liuweiheng.base.utils.HttpUtil;
import com.ourway.constant.FeiShuConstants;
import com.ourway.dao.FeiShuUserSyncMapper;
import com.ourway.dao.UserMapper;
import com.ourway.dtos.FeiShuDeptAddDTO;
import com.ourway.dtos.FeiShuUserAddDTO;
import com.ourway.dtos.FeiShuUserDeleteDTO;
import com.ourway.dtos.UserDTO;
import com.ourway.entity.FeiShuDepartment;
import com.ourway.entity.FeiShuUser;
import com.ourway.entity.FeiShuUserSync;
import com.ourway.entity.User;
import com.ourway.entity.feishu.user.FeiShuUserResponseVo;
import com.ourway.entity.vo.*;
import com.ourway.service.IFeiShuService;
import org.json.JSONException;
import org.json.JSONObject;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.*;

/**
 * FeiShu 服务类
 *
 * @author lwh
 * @date 2021/11/23
 */
@Service
public class FeiShuServiceImpl implements IFeiShuService {

    private final FeiShuConstants feiShuConstants;

    private final UserMapper userMapper;

    private final FeiShuUserSyncMapper feiShuUserSyncMapper;

    public FeiShuServiceImpl(FeiShuConstants feiShuConstants, UserMapper userMapper, FeiShuUserSyncMapper feiShuUserSyncMapper) {
        this.feiShuConstants = feiShuConstants;
        this.userMapper = userMapper;
        this.feiShuUserSyncMapper = feiShuUserSyncMapper;
    }

    /**
     * token 有效期为 2 小时，在此期间调用该接口 token 不会改变。当 token 有效期小于 30 分的时候，再次请求获取 token 的时候，会生成一个新的 token，与此同时老的 token 依然有效。
     * @return tenant_access_token
     */
    @Override
    public String getTenantAccessToken() {

        // 尝试在缓存中获取token
        String cacheToken = GlobalStrRedisUtil.getValue(GlobalStrRedisConstant.FEISHU_TENANT_ACCESS_TOKEN);
        if (!StrUtil.isEmpty(cacheToken)) {
            return cacheToken;
        }

        Map<String, String> params = new HashMap<>(2);
        params.put("app_id", feiShuConstants.getAppId());
        params.put("app_secret", feiShuConstants.getAppSecret());

        // 发送POST请求，获取tenant-access-token
        Map resultMap = HttpUtil.postWithJsonSync(feiShuConstants.getTenantAccessTokenRedirectUrl(), params, Map.class, null);

        if (null == resultMap) {
            return "";
        }

        String token = String.valueOf(resultMap.get("tenant_access_token"));
        int expire = Integer.parseInt(String.valueOf(resultMap.get("expire")));

        if (StrUtil.isBlank(token)) {
            throw new NullPointerException("tenant_access_token is null");
        }

        // 将token加入缓存，并设置过期时间
        GlobalStrRedisUtil.setValue(GlobalStrRedisConstant.FEISHU_TENANT_ACCESS_TOKEN, token, expire - 20*60);

        return String.valueOf(resultMap.get("tenant_access_token"));
    }

    @Override
    public UserAccessTokenInfoVo getUserAccessToken(String tenantAccessToken, String code) {
        Map<String, String> params = new HashMap<>(2);
        params.put("grant_type", "authorization_code");
        params.put("code", code);

        Map<String, String> header = new HashMap<>(1);
        header.put("Authorization", "Bearer " + tenantAccessToken);

        // 发送POST请求，获取tenant-access-token
        FeiShuResponseVo responseVo = HttpUtil.postWithJsonSync(feiShuConstants.getUserAccessTokenUrl(), params, FeiShuResponseVo.class, header);

        if (null == responseVo) { return null; }

        return responseVo.getData();

    }

    @Override
    public String getLoginRedirectUrl() throws UnsupportedEncodingException {
        StringBuilder url = new StringBuilder();
        String baseUrl = feiShuConstants.getLoginRedirectUrl();
        url.append(baseUrl)
                .append("?redirect_uri=").append(URLEncoder.encode(feiShuConstants.getLoginCallBackUrl(), "utf-8"))
                .append("&app_id=").append(feiShuConstants.getAppId())
                .append("&state=").append(DateUtil.date().getTime());
        return url.toString();
    }

    @Override
    public String userLogin(UserAccessTokenInfoVo userInfo) {
        String openId = userInfo.getOpen_id();
        String accessToken = userInfo.getAccess_token();
        String refreshToken = userInfo.getRefresh_token();
        int expire = userInfo.getExpires_in();
        // 检查缓存中用户信息是否过期
        boolean isLogin =  false;

        Object cacheObj =  GlobalStrRedisUtil.getHashVal(GlobalStrRedisConstant.FEISHU_USER_INFO, openId);

        Date now = DateUtil.date();

        if (null != cacheObj) {
            UserAccessTokenInfoVo userInfoCache = JSONUtil.toBean(String.valueOf(cacheObj), UserAccessTokenInfoVo.class);
            Date expireTime = userInfoCache.getWhenExpire();
            isLogin = DateUtil.compare(now, expireTime) < 0;
        }

        // 生成access_token, 并存储
        String token = StrUtil.uuid();

        // 封装缓存信息
        Calendar expireDate = DateUtil.calendar(now);
        expireDate.add(Calendar.SECOND, expire - 10 * 60);
        userInfo.setWhenExpire(DateUtil.date(expireDate));

        // 若存在用户，则更新用户信息
        if (isLogin) {
            GlobalStrRedisUtil.begin();
            GlobalStrRedisUtil.delHashKey(GlobalStrRedisConstant.FEISHU_USER_INFO, openId);
            GlobalStrRedisUtil.setHashVal(GlobalStrRedisConstant.FEISHU_USER_INFO, openId, userInfo);
            GlobalStrRedisUtil.setValue(GlobalStrRedisConstant.USER_LOGIN_TOKEN + "_" + token, JSONUtil.toJsonStr(userInfo), 30*60);
            GlobalStrRedisUtil.commit();
            return token;
        }

        // 若不存在用户，则添加用户信息
        GlobalStrRedisUtil.setHashVal(GlobalStrRedisConstant.FEISHU_USER_INFO, openId, userInfo);

        // 存储token
        GlobalStrRedisUtil.setValue(GlobalStrRedisConstant.USER_LOGIN_TOKEN + "_" + token, JSONUtil.toJsonStr(userInfo), 30*60);

        return token;
    }

    @Override
    public void replyMsg(String accessToken, String messageId, String answer, String eventId) throws JSONException {
        Map<String, String> header = new HashMap<>(1);
        header.put("Authorization", "Bearer " + accessToken);

        String url = feiShuConstants.getReplyMessageUrl(messageId);

        JSONObject jsonObject = new JSONObject();
        jsonObject.put("text", answer);

        Map<String, String> params = new HashMap<>(2);
        params.put("content", jsonObject.toString());
        params.put("msg_type", "text");

        // 输出日志

        FeiShuResponseVo responseVo = HttpUtil.postWithJsonSync(url, params, FeiShuResponseVo.class, header);

        Assert.notNull(responseVo, "调用FeiShu接口发送消息失败");

        // 将EvnetId存储起来
        GlobalStrRedisUtil.setHashVal(GlobalStrRedisConstant.FEISHU_SUBSCRIBE_EVENT_ID, eventId, "1");
    }

    @Override
    public void subScribeApproval(String accessToken) {
        Map<String, String> header = new HashMap<>(1);
        header.put("Authorization", "Bearer " + accessToken);

        String url = feiShuConstants.getSubScribeApprovalUrl();

        Map<String, String> params = new HashMap<>(1);
        params.put("approval_code", feiShuConstants.getLeaveApprovalCode());

        FeiShuResponseVo responseVo = HttpUtil.postWithJsonSync(url, params, FeiShuResponseVo.class, header);

        Assert.notNull(responseVo, "调用FeiShu接口订阅审批失败");

    }

    @Override
    public FeiShuApprovalInfoResponseVo getApprovalInfo(String accessToken, String instanceCode) {
        Map<String, String> header = new HashMap<>(1);
        header.put("Authorization", "Bearer " + accessToken);

        String url = feiShuConstants.getApprovalInstanceInfoUrl();

        Map<String, String> params = new HashMap<>(1);
        params.put("instance_code", instanceCode);

        return HttpUtil.postWithJsonSync(url, params, FeiShuApprovalInfoResponseVo.class, header);
    }

    @Override
    public void approveInstance(String accessToken, String approvalCode, String instanceCode, String openId, String userId, String taskId, String comment, String eventId) {
        approveInstance(accessToken, approvalCode, instanceCode, openId, userId, taskId, comment);

        // 将evnetId存储起来
        GlobalStrRedisUtil.setHashVal(GlobalStrRedisConstant.FEISHU_SUBSCRIBE_EVENT_ID, eventId, "1");
    }

    @Override
    public boolean approveInstance(String accessToken, String approvalCode, String instanceCode, String openId, String userId, String taskId, String comment) {
        Map<String, String> header = new HashMap<>(1);
        header.put("Authorization", "Bearer " + accessToken);

        String url = feiShuConstants.getApproveInstanceUrl();

        Map<String, String> params = new HashMap<>(5);
        params.put("instance_code", instanceCode);
        params.put("approval_code", approvalCode);
        params.put("open_id", openId);
        params.put("user_id", userId);
        params.put("task_id", taskId);

        FeiShuApprovalInfoResponseVo responseVo = HttpUtil.postWithJsonSync(url, params, FeiShuApprovalInfoResponseVo.class, header);

        return responseVo.getCode() == 0;
    }

    @Override
    public boolean sendLeaveApprovalCard(String accessToken, String replyUserId, String approvalUserId, String startTime, String endTime, String approvalName, String instanceCode, String eventId) {
        Map<String, String> header = new HashMap<>(1);
        header.put("Authorization", "Bearer " + accessToken);

        String getUserInfoUrl = feiShuConstants.getUserInfoUrl(replyUserId);

        FeiShuUserResponseVo userInfo = HttpUtil.getWithJsonSync(getUserInfoUrl, FeiShuUserResponseVo.class, header);

        Assert.notNull(userInfo, "查询FeiShu用户信息失败");

        // 生成卡片
        String replyName = userInfo.getData().getUser().getName();
        String content = getLeaveCardContent(replyName, approvalName, instanceCode);

        // 发送卡片消息
        String sendMessageUrl = feiShuConstants.getSendMessageUrl("open_id");
        Map<String, String> sendMessageParams = new HashMap<>(3);
        sendMessageParams.put("receive_id", approvalUserId);
        sendMessageParams.put("content", content);
        sendMessageParams.put("msg_type", "interactive");
        FeiShuSendMessageResponseVo responseVo = HttpUtil.postWithJsonSync(sendMessageUrl, sendMessageParams, FeiShuSendMessageResponseVo.class, header);

        if (responseVo.getCode() != 0) {
            return false;
        }

        // 将evnetId存储起来
        GlobalStrRedisUtil.setHashVal(GlobalStrRedisConstant.FEISHU_SUBSCRIBE_EVENT_ID, eventId, "1");
        return true;
    }

    @Override
    public boolean updateLeaveApprovalCard(String replyUserId, String approvalName, String messageId) {
        String accessToken = getTenantAccessToken();

        Map<String, String> header = new HashMap<>(1);
        header.put("Authorization", "Bearer " + accessToken);

        String getUserInfoUrl = feiShuConstants.getUserInfoUrl(replyUserId);

        FeiShuUserResponseVo userInfo = HttpUtil.getWithJsonSync(getUserInfoUrl, FeiShuUserResponseVo.class, header);

        // 生成卡片
        String replyName = userInfo.getData().getUser().getName();

        String content = getApproveLeaveCardContent(replyName, approvalName);

        String sendMessageUrl = feiShuConstants.getUpdateLeaveMessageCardUrl(messageId);

        Map<String, String> sendMessageParams = new HashMap<>(1);
        sendMessageParams.put("content", content);

        FeiShuSendMessageResponseVo responseVo = HttpUtil.patchWithJsonSync(sendMessageUrl, sendMessageParams, FeiShuSendMessageResponseVo.class, header);

        return responseVo.getCode() == 0;
    }

    @Override
    public FeiShuSearchChildDeptVo getAllDepartment() {
        String accessToken = getTenantAccessToken();

        Map<String, String> header = new HashMap<>(1);
        header.put("Authorization", "Bearer " + accessToken);

        String deptId = "0";

        String fetchChild = "true";

        String url = feiShuConstants.getSearchChildDepartmentUrl(deptId) + "?fetch_child=" + fetchChild;

        String response = HttpUtil.getWithJsonSync(url, String.class, header);

        FeiShuNormalResponseVo responseVo = JSONUtil.toBean(response, FeiShuNormalResponseVo.class);

        return JSONUtil.toBean(responseVo.getData().toString(), FeiShuSearchChildDeptVo.class);
    }

    @Override
    public FeiShuSearchUserByDeptResponseVo getAllUserByDepartment(String deptId) throws JSONException {
        String accessToken = getTenantAccessToken();
        Map<String, String> header = new HashMap<>(1);
        header.put("Authorization", "Bearer " + accessToken);

        deptId = StrUtil.isBlank(deptId) ? "0" : deptId;

        String url = feiShuConstants.getSearchUserByParentDepartmentUrl(deptId);

        String response = HttpUtil.getWithJsonSync(url, String.class, header);

        FeiShuNormalResponseVo responseVo = JSONUtil.toBean(response, FeiShuNormalResponseVo.class);

        return JSONUtil.toBean(responseVo.getData().toString(), FeiShuSearchUserByDeptResponseVo.class);
    }

    @Override
    public FeiShuDepartment createDept(FeiShuDeptAddDTO param) throws JSONException {
        boolean result = false;

        String accessToken = getTenantAccessToken();
        Map<String, String> header = new HashMap<>(1);
        header.put("Authorization", "Bearer " + accessToken);

        String url = feiShuConstants.getCreateDepartmentUrl();

        String response = HttpUtil.postWithJsonSync(url, param, String.class, header);

        FeiShuCreateDeptResponseVo responseVo = JSONUtil.toBean(response, FeiShuCreateDeptResponseVo.class);

        result = responseVo.getCode() == 0;

        if (result) {
            return responseVo.getData().getDepartment();
        }

        return null;
    }

    @Override
    public PageResultDto<UserDTO> getUserList(int pageIndex, int pageSize) {

///        IPage<User> userPage = new Page<>(pageIndex, pageSize);
///        List<User> userList = userMapper.selectPage(userPage, null).getRecords();
///        List<UserDTO> userDTOList = BeanCopyUtil.copyListProperties(userList, UserDTO::new);

        // 根据分页查询用户列表
///        List<UserDTO> userDTOList = userMapper.selectJoinPage(new Page<>(pageIndex, pageSize), UserDTO.class,
///                new MPJLambdaWrapper<User>()
///                        .selectAll(User.class)
///                        .selectAs(Department::getName, UserDTO::getDepartmentName)
///                        .leftJoin(Department.class, Department::getDepartmentId, User::getDepartmentId))
///                .getRecords();

        List<UserDTO> userDTOList = userMapper.findAll(pageIndex, pageSize);

        // 查询总用户列表数量
        int totalCount = userMapper.selectCount(null);
        return PageResultDto.<UserDTO>builder()
                .dataList(userDTOList)
                .totalCount(totalCount)
                .build();
    }

    @Override
    public List<UserDTO> getAllUser() {
        return BeanCopyUtil.copyListProperties(userMapper.selectList(null), UserDTO::new);
    }

    @Override
    public Boolean checkUserNo(String userId, String userNo) {
        User user = new LambdaQueryChainWrapper<>(userMapper).eq(User::getUserNo, userNo).one();
        if (null == user) {
            return Boolean.FALSE;
        }
        if (StrUtil.isNotBlank(userId) && user.getUserId().equals(userId)) {
            return Boolean.FALSE;
        }

        return Boolean.TRUE;
    }

    @Override
    public FeiShuUserSync getUserSyncInfo(String userId) {
        return new LambdaQueryChainWrapper<>(feiShuUserSyncMapper)
                .eq(FeiShuUserSync::getUserId, userId)
                .one();
    }

    @Override
    public FeiShuUser getFeiShuUser(String openId) {
        String tenantAccessToken = getTenantAccessToken();
        Map<String, String> header = new HashMap<>(1);
        header.put("Authorization", "Bearer " + tenantAccessToken);
        String url = feiShuConstants.getUserInfoUrl(openId);
        String response = HttpUtil.getWithJsonSync(url, String.class, header);
        FeiShuNormalResponseVo responseVo = JSONUtil.toBean(response, FeiShuNormalResponseVo.class);
        boolean result = responseVo.getCode() == 0;

        if (result) {
            return JSONUtil.toBean(responseVo.getData().toString(), FeiShuUser.class);
        }
        return null;
    }

    @Override
    public FeiShuUser createFeiShuUser(FeiShuUserAddDTO feiShuUserAddDTO) {
        System.out.println(JSONUtil.toJsonStr(feiShuUserAddDTO));
        String url = feiShuConstants.getCreateUserUrl();
        String accessToken = getTenantAccessToken();
        Map<String, String> header = new HashMap<>(1);
        header.put("Authorization", "Bearer " + accessToken);
        String response = HttpUtil.post(url, feiShuUserAddDTO, String.class, header);

        FeiShuNormalResponseVo responseVo = JSONUtil.toBean(response, FeiShuNormalResponseVo.class);

        boolean result = responseVo.getCode() == 0;

        System.out.println(responseVo);
        Assert.isTrue(result, "同步创建飞书用户失败");

        return JSONUtil.toBean(responseVo.getData().toString(), FeiShuUser.class);
    }

    @Override
    public Boolean deleteUser(FeiShuUserDeleteDTO userDeleteDTO) {
        String url = feiShuConstants.getDeleteUserUrl(userDeleteDTO.getUserId());
        String accessToken = getTenantAccessToken();
        Map<String, String> header = new HashMap<>(1);
        header.put("Authorization", "Bearer " + accessToken);
        String response = HttpUtil.delete(url, userDeleteDTO, String.class, header);

        FeiShuNormalResponseVo responseVo = JSONUtil.toBean(response, FeiShuNormalResponseVo.class);

        boolean result = responseVo.getCode() == 0;

        Assert.isTrue(result, "同步删除飞书用户失败");

        return Boolean.TRUE;
    }

    private String getLeaveCardContent(String replyName, String approvalName, String instanceCode) {
        return "{\"config\":{\"wide_screen_mode\":true},\"header\":{\"title\":{\"tag\":\"plain_text\",\"content\":\"你有一个休假申请待审批\"}},\"elements\":[{\"tag\":\"div\",\"fields\":[{\"is_short\":true,\"text\":{\"tag\":\"lark_md\",\"content\":\"**申请人**\\n"+ replyName +"\"}},{\"is_short\":true,\"text\":{\"tag\":\"lark_md\",\"content\":\"**事件类型：**\\n"+ approvalName +"\"}},{\"is_short\":false,\"text\":{\"tag\":\"lark_md\",\"content\":\"\"}},{\"is_short\":false,\"text\":{\"tag\":\"lark_md\",\"content\":\"\"}}]},{\"tag\":\"hr\"},{\"tag\":\"action\",\"layout\":\"bisected\",\"actions\":[{\"tag\":\"button\",\"text\":{\"tag\":\"plain_text\",\"content\":\"批准\"},\"type\":\"primary\",\"value\":{\"chosen\":\"approve\",\"instanceCode\":\""+ instanceCode +"\"}},{\"tag\":\"button\",\"text\":{\"tag\":\"plain_text\",\"content\":\"拒绝\"},\"type\":\"primary\",\"value\":{\"chosen\":\"decline\",\"instanceCode\":\""+ instanceCode +"\"}}]}]}";
    }

    private String getApproveLeaveCardContent(String replyName, String approvalName) {
        return "{\"config\":{\"wide_screen_mode\":true},\"header\":{\"title\":{\"tag\":\"plain_text\",\"content\":\"你的休假申请已通过\"}},\"elements\":[{\"tag\":\"div\",\"fields\":[{\"is_short\":true,\"text\":{\"tag\":\"lark_md\",\"content\":\"**申请人**\\n"+ replyName +"\"}},{\"is_short\":true,\"text\":{\"tag\":\"lark_md\",\"content\":\"**事件类型：**\\n"+ approvalName +"\"}},{\"is_short\":false,\"text\":{\"tag\":\"lark_md\",\"content\":\"\"}}]}]}";
    }
}
